dojo.provide("dojo.rpc.RpcService");
 
 dojo.declare("dojo.rpc.RpcService", null, {
         constructor: function(args){
                 //summary:
                 //Take a string as a url to retrieve an smd or an object that is an smd or partial smd to use
                 //as a definition for the service
                 //
                 //      args: object
	                //              Takes a number of properties as kwArgs for defining the service.  It also
	                //              accepts a string.  When passed a string, it is treated as a url from
	                //              which it should synchronously retrieve an smd file.  Otherwise it is a kwArgs
	                //              object.  It accepts serviceUrl, to manually define a url for the rpc service
	                //              allowing the rpc system to be used without an smd definition. strictArgChecks
	                //              forces the system to verify that the # of arguments provided in a call
	                //              matches those defined in the smd.  smdString allows a developer to pass
	                //              a jsonString directly, which will be converted into an object or alternatively
	                //              smdObject is accepts an smdObject directly.
	                //                             
	                if(args){
	                        //if the arg is a string, we assume it is a url to retrieve an smd definition from
	                        if( (dojo.isString(args)) || (args instanceof dojo._Url)){
	                                if (args instanceof dojo._Url){
	                                        var url = args + "";
	                                }else{
	                                        url = args;
	                                }
	                                var def = dojo.xhrGet({
	                                        url: url,
	                                        handleAs: "json-comment-optional",
	                                        sync: true
	                                });
	                               
	                                def.addCallback(this, "processSmd");
	                                def.addErrback(function() {
	                                        throw new Error("Unable to load SMD from " + args);
	                                });
	
	                        }else if(args.smdStr){
	                                this.processSmd(dojo.eval("("+args.smdStr+")"));
	                        }else{
	                                // otherwise we assume it's an arguments object with the following
	                                // (optional) properties:
	                                //      - serviceUrl
	                                //      - strictArgChecks
	                                //      - smdStr
	                                //      - smdObj
	
	                                if(args.serviceUrl){
	                                        this.serviceUrl = args.serviceUrl;
	                                }
	
	                                this.timeout = args.timeout || 3000;
	
	                                if("strictArgChecks" in args){
	                                        this.strictArgChecks = args.strictArgChecks;
	                                }
	
	                                this.processSmd(args);
	                        }
	                }
	        },
	
	        strictArgChecks: true,
	        serviceUrl: "",
	
	        parseResults: function(obj){
	                // summary
	                //              parse the results coming back from an rpc request.  this
	                //              base implementation, just returns the full object
	                //              subclasses should parse and only return the actual results
	                //      obj: Object
	                //              Object that is the return results from an rpc request
	                return obj;
	        },
	
	        errorCallback: function(/* dojo.Deferred */ deferredRequestHandler){
	                // summary:
	                //              create callback that calls the Deferres errback method
	                //      deferredRequestHandler: Deferred
	                //              The deferred object handling a request.
	                return function(data){
	                        deferredRequestHandler.errback(new Error(data.message));
	                };
	        },
	
	        resultCallback: function(/* dojo.Deferred */ deferredRequestHandler){
	                // summary:
	                //              create callback that calls the Deferred's callback method
	                //      deferredRequestHandler: Deferred
	                //              The deferred object handling a request.
	
	                var tf = dojo.hitch(this,
	                        function(obj){
	                                if(obj.error!=null){
	                                        var err;
	                                        if(typeof obj.error == 'object'){
	                                                err = new Error(obj.error.message);
	                                                err.code = obj.error.code;
 	                                                err.error = obj.error.error;
 	                                        }else{
 	                                                err = new Error(obj.error);
 	                                        }
 	                                        err.id = obj.id;
 	                                        err.errorObject = obj;
 	                                        deferredRequestHandler.errback(err);
 	                                }else{
 	                                        deferredRequestHandler.callback(this.parseResults(obj));
 	                                }
 	                        }
 	                );
 	                return tf;
 	        },
 	
 	        generateMethod: function(/*string*/ method, /*array*/ parameters, /*string*/ url){
 	                // summary:
 	                //              generate the local bind methods for the remote object
 	                //      method: string
 	                //              The name of the method we are generating
 	                //      parameters: array
 	                //              the array of parameters for this call.
 	                //      url: string
 	                //              the service url for this call
 	
 	                return dojo.hitch(this, function(){
 	                        var deferredRequestHandler = new dojo.Deferred();
 	
 	                        // if params weren't specified, then we can assume it's varargs
 	                        if( (this.strictArgChecks) &&
 	                                (parameters != null) &&
 	                                (arguments.length != parameters.length)
 	                        ){
 	                                // put error stuff here, no enough params
 	                                throw new Error("Invalid number of parameters for remote method.");
 	                        }else{
 	                                this.bind(method, dojo._toArray(arguments), deferredRequestHandler, url);
 	                        }
 	
 	                        return deferredRequestHandler;
 	                });
 	        },
 	
 	        processSmd: function(object){
 	                // summary:
 	                //              callback method for reciept of a smd object.  Parse the smd
 	                //              and generate functions based on the description
 	                //      object:
 	                //              smd object defining this service.
 	
 	                if(object.methods){
 	                        dojo.forEach(object.methods, function(m){
 	                                if(m && m.name){
 	                                        this[m.name] = this.generateMethod(     m.name,
 	                                                                                m.parameters,
 	                                                                                m.url||m.serviceUrl||m.serviceURL);
 	                                        if(!dojo.isFunction(this[m.name])){
 	                                                throw new Error("RpcService: Failed to create" + m.name + "()");
 	                                                /*console.debug("RpcService: Failed to create", m.name, "()");*/
 	                                        }
 	                                }
 	                        }, this);
 	                }
 	
 	                this.serviceUrl = object.serviceUrl||object.serviceURL;
 	                this.required = object.required;
 	                this.smd = object;
 	        }
 	});